package game.johan.engine;

/*
Copyright (C) 2013 Johan Ceuppens

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/


import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Environment;
import android.view.Display;
import android.view.WindowManager;

/*
 *  This class provides a bitmap surface and a
 *  ByteBuffer, you can render it and draw bytebuffers
 *  and bitmaps on it
 * 
 *  The Bytebuffer _bb gets bitmapped on _canvas with draawbbOn,
 *
 *  The bitmap can be written on with drawOn methods
 */
class Surface
{
private Bitmap _bitmap;
private ByteBuffer _bb,_loadbb;
private Caretaker<String> _fileresourcecaretaker = new Caretaker<String>();

private int surfacew = 0;
private int surfaceh = 0;

public Surface(int w, int h) {
	surfacew = w;
	surfaceh = h;
	init(w,h);
}

public Surface(Bitmap b) {
	surfacew = b.getWidth();
	surfaceh = b.getHeight();
	initWithBitmap(b);
}

private Tuple getScreenWH(Context ct) {
	WindowManager wm = (WindowManager) ct.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay(); 
    int screenWidth = display.getWidth();
    int screenHeight = display.getHeight();
    //setMeasuredDimension(screenWidth, screenHeight);	
    return new Tuple(screenWidth,screenHeight);
}

private ByteBuffer init(int ww, int hh) {
  synchronized (this) {
    _bitmap = Bitmap.createBitmap(ww, hh, Bitmap.Config.RGB_565);
    _bb = ByteBuffer.allocateDirect(ww * hh * 2);
    
    return _bb;
  }
}

private ByteBuffer initWithBitmap(Bitmap b) {
  synchronized (this) {
    _bitmap = b;
    return _bb;
  }
}

public void lockCanvasBase(EngineBase base)
{
	base.lockCanvas1();
}

public void unlockCanvasBase(Canvas canvas, EngineBase base)
{
	unlockCanvas1(canvas, base);
}

public void drawOnCanvasBase(Canvas canvas, EngineBase base)
{
	drawOnCanvas1(_bitmap, canvas);
}

public void drawOnCanvasBaseBitmap(Bitmap b, Canvas canvas)
{
	drawOnCanvas1(b,canvas);
}

protected void lockCanvas1(Canvas canvas, EngineBase base) {
	  canvas = base.getHolder().lockCanvas(null);
}

protected void drawOnCanvas1(Bitmap b, Canvas canvas) {
	  canvas.drawBitmap(b, 0, 0, null);
}

protected void drawOnCanvasxy1(int xx, int yy, Bitmap b, Canvas canvas) {
	  canvas.drawBitmap(b, xx, yy, null);
}

protected void unlockCanvas1(Canvas canvas, EngineBase base) {
	  base.getHolder().unlockCanvasAndPost(canvas);
}

public void draw(EngineBase base) {
	render(base);
}

public void render(EngineBase base) {
  synchronized (this) {
    try {
    	base.lockCanvas1();
    	base.drawOnCanvas1(_bitmap);
    	base.unlockCanvas1();
    } catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
    }
  }
}

public void drawbbOn() {
	_bitmap.copyPixelsFromBuffer(_bb);
}

public void drawloadbbOn() {
	_bitmap.copyPixelsFromBuffer(_loadbb);
}

private void release() {
  synchronized (this) {
    _bitmap.recycle();
    _bitmap = null;
    _bb = null;
  }
}


public byte[] toByteArray() {
	byte[] bytes = new byte[toBitmapSurface()];
	//try {
	    _bb.get(bytes, 0, bytes.length);
	//}
	//catch (BufferUnderflowException e)
	//{
    	//System.out.println("Message "+e.getMessage());
    	//e.printStackTrace();
	//}
	return bytes;
	//do something with bytes
}

public byte[] toArray2() {
	byte[] bytes = new byte[toBitmapSurface()];
	return bytes;
	//do something with bytes
}

public int toBitmapSurface() {
	int size = _bitmap.getRowBytes() * _bitmap.getHeight();
	_bb = ByteBuffer.allocate(size);
	_bitmap.copyPixelsToBuffer(_bb);
	return size;
}

/*
 * ww,hh is width and height of bb arg
 */
public void drawOnByteBuffer(ByteBuffer bb) {
	int ww = surfacew;
	int hh = surfaceh;
	int sizew = _bitmap.getRowBytes();
	int i = 0, j = 0, kw = 0;
	for ( ; i < hh; i++) {
		for( ; j < ww; j++) {
			_bb.put(kw,bb.get(i*ww+j));
			kw++;
		}
		kw += sizew-ww;
		j = 0;
	}
}
	
	public void toByteBuffer(byte[] ba) {
		int ww = surfacew;
		int hh = surfaceh;
		int sizew = _bitmap.getRowBytes();
		int i = 0, j = 0, kw = 0;
		for ( ; i < hh; i++) {
			for( ; j < ww; j++) {
				_bb.get(ba, 0, ba.length);
				kw++;
			}
			kw += sizew-ww;
			j = 0;
		}
	}
	
	/*
	 * This draws the byte[] ba on the _bitmap surface with offsets xx,yy and
	 * width and height ww,hh.
	 * The bitmap can be loaded with the load() method
	 * and provide a background surface
	 */
	public void drawOnByteArray(int xx, int yy, int ww, int hh, byte[] ba) {
		toByteBuffer(ba);
		drawOnByteBufferxy(xx,yy,ww,hh,_bb);
		//done above _bitmap.copyPixelsFromBuffer(_bb);			
	}
	
	/*
	 * The following method write the bytebuffer after load()
	 * over the surface _bitmap
	 * There also is a loadanddraw method in this file
	 */
	public void drawOnLoadByteBuffer(int xx, int yy, int ww, int hh) {
		drawOnByteBufferxy(xx,yy,ww,hh,_loadbb);
		//done above _bitmap.copyPixelsFromBuffer(_bb);			
	}

/*
 * ww,hh is width and height of bb arg
 * xx,yy is position of in bb stored image to draw on _bb
 */
public void drawOnByteBufferxy(int xx, int yy, int ww, int hh, ByteBuffer bb) {
	int sizew = _bitmap.getRowBytes();
	//int sizeh = _bitmap.getHeight();
	int i = 0, j = 0, kw = xx+yy*ww;
	for ( ; i < hh; i++) {
		for( ; j < ww; j++) {
			_bb.put(kw,bb.get(i*ww+j));
			kw++;
		}
		kw += sizew-ww;
		j = 0;
	}
	
	
}

/*
 * The following method provides a seperate bytebuffer _loadbb
 * which can be overwritten on the main _bitmap surface
 * with the drawOnByteBufferxy method (bb as an ByteBuffer argument)
 */

public void load(String gfxfilename)
{

	if (_loadbb != null)
		return;
	
	FileInputStream in;
	BufferedInputStream buf;
	try {
		if (gfxfilename == "") 
			in = new FileInputStream(Environment.getExternalStorageDirectory().getPath() + "stub.png");
		else
			in = new FileInputStream(Environment.getExternalStorageDirectory().getPath() + gfxfilename);
		
		buf = new BufferedInputStream(in);
	    byte[] bMapArray = new byte[buf.available()];
	    buf.read(bMapArray);
	    _bitmap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
	    _bitmap.copyPixelsToBuffer(_loadbb);
    
	    if (in != null) {
	    	in.close();
	    }
	    if (buf != null) {
	    	buf.close();
	    }
	} catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
	}
}

public void reload(String gfxfilename)
{
	
	FileInputStream in;
	BufferedInputStream buf;
	try {
		if (gfxfilename == "") 
			in = new FileInputStream(Environment.getExternalStorageDirectory().getPath() + "stub.png");
		else
			in = new FileInputStream(Environment.getExternalStorageDirectory().getPath() + gfxfilename);
		
		buf = new BufferedInputStream(in);
	    byte[] bMapArray = new byte[buf.available()];
	    buf.read(bMapArray);
	    _bitmap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
	    _bitmap.copyPixelsToBuffer(_loadbb);
    
	    if (in != null) {
	    	in.close();
	    }
	    if (buf != null) {
	    	buf.close();
	    }
	} catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
	}
}

public void loadAndDraw(String gfxfilename, int xx, int yy, int ww, int hh)
{
	load(gfxfilename);
	_fileresourcecaretaker.save(gfxfilename);
	drawOnLoadByteBuffer(xx,yy,ww,hh);
}

public void reloadAndDraw(String gfxfilename, int xx, int yy, int ww, int hh)
{
	reload(gfxfilename);
	_fileresourcecaretaker.save(gfxfilename);
	drawOnLoadByteBuffer(xx,yy,ww,hh);
}

}